from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QLabel, QSpinBox, QSplitter, QApplication, QCheckBox, QFrame, QGridLayout, QComboBox, \
    QLineEdit, QDoubleSpinBox


class SettingSplitter(QSplitter):
    def __init__(self, orientation=Qt.Horizontal):
        super(SettingSplitter, self).__init__(orientation)
        self.setChildrenCollapsible(False)
        self.setHandleWidth(0)


class LabeledWidget(QSplitter):
    """
    一个带有标签的基本控件类。继承自QSplitter。

    该类创建了一个带有标签的控件，用于在GUI中进行设置。
    它主要包含一个标签（QLabel）和一个具体的控件（例如QSpinBox、QComboBox等）。

    参数:
    - ui_key: 用于标识UI元素的键值，类型为可哈希对象。
    - label_text: 标签文本内容，类型为字符串。
    - orientation: 组合框的排列方向，类型为Qt的Orientation枚举值（Horizontal或Vertical）。
    """

    def __init__(self, ui_key=None, label_text=None, orientation=Qt.Horizontal):
        super(LabeledWidget, self).__init__(orientation)

        if ui_key is None or label_text is None:
            raise ValueError("ui_key and label_text cannot be None")

        self.left_widget = QLabel()
        self.right_widget = None  # 子类需要设置具体的控件

        self.left_widget.setText(label_text)
        self.addWidget(self.left_widget)

        self.ui_key = ui_key

        self.right_widget = self._init_right_widget()
        self.right_widget.setFixedSize(150, 35)
        self.addWidget(self.right_widget)

    def get_value(self):
        """
        获取当前控件的值。

        参数: 无

        返回: 当前控件的值，类型取决于具体控件。
        """
        return self.right_widget.value()

    def set_value(self, val):
        """
        设置控件的当前值。

        参数:
        - val: 要设置的值，类型取决于具体控件。

        返回: 无
        """
        self.right_widget.setValue(val)

    def _init_right_widget(self):
        """
        初始化右侧控件。

        参数: 无

        返回: 初始化后的右侧控件。
        """
        raise NotImplementedError("Subclasses must implement this method")


class LabeledSpinbox(LabeledWidget):
    def __init__(self, ui_key=None, label_text=None, spinbox_range=(0, 999999), orientation=Qt.Horizontal):
        self.spinbox_range = spinbox_range
        if not (isinstance(spinbox_range, tuple) and len(spinbox_range) == 2):
            raise ValueError("spinbox_range must be a tuple of length 2")
        super(LabeledSpinbox, self).__init__(ui_key, label_text, orientation)

    def get_value(self):
        if isinstance(self.right_widget, QSpinBox):
            return self.right_widget.value()
        elif isinstance(self.right_widget, QDoubleSpinBox):
            return self.right_widget.value()

    def set_value(self, val):
        if isinstance(self.right_widget, QSpinBox):
            self.right_widget.setValue(val)
        elif isinstance(self.right_widget, QDoubleSpinBox):
            self.right_widget.setValue(val)

    def _init_right_widget(self):
        right_widget = None
        step = 1
        if isinstance(self.spinbox_range[0], int):
            right_widget = QSpinBox()
            step = 1
        elif isinstance(self.spinbox_range[0], float):
            right_widget = QDoubleSpinBox()
            right_widget.setDecimals(4)
            step = 0.0001

        right_widget.setFixedSize(150, 35)
        right_widget.setRange(self.spinbox_range[0], self.spinbox_range[1])
        right_widget.setSingleStep(step)
        return right_widget


class LabeledCombox(LabeledWidget):
    def __init__(self, ui_key=None, label_text=None, item_list=[], orientation=Qt.Horizontal):
        self.item_list = item_list
        if not isinstance(item_list, list):
            raise ValueError("item_list must be a list")
        super(LabeledCombox, self).__init__(ui_key, label_text, orientation)

    def get_value(self):
        return self.right_widget.currentText()

    def set_value(self, val):
        index = self.right_widget.findText(val)
        if index >= 0:
            self.right_widget.setCurrentIndex(index)

    def _init_right_widget(self):
        right_widget = QComboBox()
        right_widget.addItems(self.item_list)
        return right_widget


class LabeledCheckbox(LabeledWidget):
    def __init__(self, ui_key=None, label_text=None, orientation=Qt.Horizontal):
        super(LabeledCheckbox, self).__init__(ui_key, label_text, orientation)

    def get_value(self):
        return self.right_widget.isChecked()

    def set_value(self, val):
        self.right_widget.setChecked(val)

    def _init_right_widget(self):
        right_widget = QCheckBox()
        return right_widget


class LabeledLineEdit(LabeledWidget):
    def __init__(self, ui_key=None, label_text=None, orientation=Qt.Horizontal):
        super(LabeledLineEdit, self).__init__(ui_key, label_text, orientation)

    def get_value(self):
        return self.right_widget.text()

    def set_value(self, val):
        self.right_widget.setText(val)

    def _init_right_widget(self):
        right_widget = QLineEdit()
        return right_widget


class CheckBoxedSpinbox(SettingSplitter):
    """
    一个组合了复选框和旋转框的控件类。

    本类继承自SettingSplitter，用于创建一个带复选框的旋转选择器，常用于可选的数值输入场景。

    参数:
    - ui_key: 用于标识UI元素的键。
    - label_text: 显示在复选框旁边的文本标签（默认为None）。
    - spinbox_range: 旋转框的数值范围，默认为(0, 999999)。
    - orientation: 设置Splitter的方向，默认为水平方向（Qt.Horizontal）。
    """

    def __init__(self, ui_key, label_text=None, spinbox_range=(0, 999999), orientation=Qt.Horizontal):
        # 初始化父类和控件属性
        super(CheckBoxedSpinbox, self).__init__(orientation)
        self.left_widget = QCheckBox()  # 创建复选框
        self.right_widget = QSpinBox()  # 创建旋转框
        self.right_widget.setFixedSize(150, 35)  # 固定旋转框大小
        if label_text:  # 如果提供了label_text
            self.left_widget.setText(label_text)  # 设置复选框的显示文本
        self.right_widget.setRange(spinbox_range[0], spinbox_range[1])  # 设置旋转框的数值范围
        self.addWidget(self.left_widget)  # 将复选框添加到Splitter中
        self.addWidget(self.right_widget)  # 将旋转框添加到Splitter中
        self.ui_key = ui_key  # 设置UI键
        # 当复选框的选中状态改变时，设置旋转框是否启用
        self.left_widget.toggled.connect(self.right_widget.setEnabled)

    def get_value(self):
        """
        获取当前旋转框的值。

        返回:
        - 当前旋转框的数值。
        """
        return self.right_widget.value()

    def set_value(self, val):
        """
        设置旋转框的值。

        参数:
        - val: 要设置的数值。
        """
        self.right_widget.setValue(val)


class CheckBoxedLineEdit(SettingSplitter):
    """
    CheckBoxedLineEdit类是SettingSplitter的子类，用于创建一个组合控件，该控件包含一个复选框和一个文本输入框。

    参数:
    - ui_key: 控件的唯一标识符，用于识别控件。
    - label_text: 可选参数，指定复选框的显示文本。
    - orientation: 可选参数，指定控件的布局方向，默认为水平布局。

    该类通过控件布局将一个复选框和一个文本输入框组合在一起，并且复选框的选中状态控制文本框的启用状态。
    """

    def __init__(self, ui_key, label_text=None, orientation=Qt.Horizontal):
        # 调用父类构造方法，初始化布局方向。
        super(CheckBoxedLineEdit, self).__init__(orientation)

        # 初始化左部控件为复选框。
        self.left_widget = QCheckBox()
        # 初始化右部控件为文本输入框，并设置固定大小。
        self.right_widget = QLineEdit()
        self.right_widget.setFixedSize(150, 35)

        # 设置复选框的显示文本。
        self.left_widget.setText(label_text)

        # 将复选框和文本输入框添加到布局中。
        self.addWidget(self.left_widget)
        self.addWidget(self.right_widget)

        # 保存ui_key用于识别控件。
        self.ui_key = ui_key

        # 连接复选框的选中状态改变信号到文本框的启用状态槽函数。
        self.left_widget.toggled.connect(self.right_widget.setEnabled)

    def get_value(self):
        """
        获取文本输入框的当前值。

        返回: 文本输入框的值。
        """
        return self.right_widget.value()

    def set_value(self, val):
        """
        设置文本输入框的值。

        参数:
        - val: 要设置的值。
        """
        self.right_widget.setValue(val)


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    frame = QFrame()
    layout = QGridLayout(frame)
    a = LabeledSpinbox(ui_key=None, label_text="人数")
    b = CheckBoxedSpinbox(ui_key=None, label_text="人数")
    try:
        layout.addWidget(a)
        layout.addWidget(b)
        frame.show()
    except Exception as e:
        print(e)
    app.exec_()
